﻿using System.Collections.Generic;
using System.Linq;
using SymbolIntegration.core.Helpers;

namespace SymbolIntegration.core.Expressions
{
    public class VariableExpression : Expression
    {
        private readonly string m_Var;

        static internal Expression ParseItself(string expression, int position, int size)
        {
            string sValue = expression.Substring(position, size);
            if (sValue.Length == 0)
            {
                throw new ParseException("String is empty", expression, position, size, 0);
            }

            if (!char.IsLetter(sValue[0]))
            {
                throw new ParseException("Varible must start with number", expression, position, size, 0);
            }
            if (!sValue.All(char.IsLetterOrDigit))
            {
                throw new ParseException("Varible name must be number or digit", expression, position, size, 0);
            }

            var result = new VariableExpression(sValue);
            return result;
        }

        public VariableExpression(string variable)
        {
            m_Var = variable;
            m_Cache.Varibles.Add(variable);
            m_Cache.IsValid = true;
        }

        public override ExpressionType Type
        {
            get { return ExpressionType.Variable;}
        }

        public override Expression Clone()
        {
            var cloned = new VariableExpression (m_Var);
            InnerCopy(cloned);
            return cloned;
        }

        public override Expression Substitute(Dictionary<string, Expression> varValues)
        {
            Expression value;
            Expression result = varValues.TryGetValue(m_Var, out value) ? 
                value.InvertNegate(IsNegate) : this;
            return result;
        }

        public override Expression Differentiate(string variable)
        {
            Expression result = variable != m_Var ? 
                new ConstantExpression(0) : new ConstantExpression(1);
            InnerCopy(result);
            return result;
        }

        public override Expression Normalize()
        {
            return this;
        }

        public override bool IsEqual(Expression right)
        {
            bool result = false;
            do
            {
                if (right.IsNegate != IsNegate)
                {
                    break;
                }

                var var = right as VariableExpression;
                if (var == null)
                {
                    break;
                }

                if (m_Var != var.m_Var)
                {
                    break;
                }

                result = true;
            }
            while (false);

            return result;
        }

        public override Polinom FormatToPolinomAsDependentFromVariable(string variable)
        {
            var polinom = new Polinom(variable);
            if (m_Var == variable)
            {
                polinom.SetConstantAt(1, new ConstantExpression(1).InvertNegate(IsNegate).Normalize());
            }
            else
            {
                polinom.SetConstantAt(0, Clone());
            }
            return polinom;
        }

        public override string ToString()
        {
            var result = IsNegate ? "-" : "";
            result += m_Var;
            return result;
        }

        protected internal override void RebuildDependancyCache()
        {
            m_Cache.Varibles.Clear();
            m_Cache.Varibles.Add(m_Var);
            m_Cache.IsValid = true;
        }

        protected internal override void FillExpressionStatistic(ExpressionStatistic statistic, string integrateVariable)
        {
            //Nothing to fill
        }

        protected internal override Expression IntegrateAsDependentFromVar(string variable, IIntegrator integrator)
        {
            double constant = IsNegate ? -0.5 : 0.5;
            var result = new ConstantExpression(constant)*new VariableExpression(m_Var)*new VariableExpression(m_Var);
            return result;
        }
    }
}
